import os
import hashlib
import time
import logging
from concurrent.futures import ThreadPoolExecutor, as_completed
from typing import Dict, Any, Optional, Callable

logger = logging.getLogger(__name__)


class ParallelHashCalculator:
    """
    并行哈希计算器，使用多线程计算文件的哈希值
    """
    def __init__(self, max_workers: int = None, hash_algorithm: str = 'md5'):
        """
        初始化并行哈希计算器
        :param max_workers: 最大工作线程数，如果为None则使用默认值
        :param hash_algorithm: 哈希算法，默认为'md5'
        """
        self.max_workers = max_workers or os.cpu_count() or 4
        self.hash_algorithm = hash_algorithm
        
        # 验证哈希算法是否支持
        if self.hash_algorithm not in hashlib.algorithms_available:
            logger.warning(f'不支持的哈希算法: {self.hash_algorithm}，将使用默认的md5算法')
            self.hash_algorithm = 'md5'
            
    def _calculate_file_hash(self, file_path: str, chunk_size: int = 8192) -> str:
        """
        计算单个文件的哈希值
        :param file_path: 文件路径
        :param chunk_size: 读取文件的块大小
        :return: 文件哈希值（十六进制字符串）
        """
        if not os.path.isfile(file_path):
            logger.warning(f'文件不存在: {file_path}')
            return ''
            
        try:
            # 创建哈希对象
            hash_obj = hashlib.new(self.hash_algorithm)
            
            # 分块读取文件并更新哈希值
            with open(file_path, 'rb') as f:
                while True:
                    chunk = f.read(chunk_size)
                    if not chunk:
                        break
                    hash_obj.update(chunk)
                    
            # 返回十六进制哈希值
            return hash_obj.hexdigest()
        except PermissionError:
            logger.warning(f'没有权限读取文件: {file_path}')
        except Exception as e:
            logger.error(f'计算文件哈希值时出错: {file_path}, 错误: {str(e)}')
            
        return ''
        
    def calculate_hashes(self, file_info_list: list, progress_callback: Optional[Callable[[int, int], None]] = None, 
                        stop_event: Optional[Callable[[], bool]] = None) -> list:
        """
        并行计算多个文件的哈希值
        :param file_info_list: 文件信息列表，每个元素是包含'path'键的字典
        :param progress_callback: 进度回调函数，接收已处理文件数和总文件数
        :param stop_event: 停止事件函数，返回True表示需要停止处理
        :return: 更新后的文件信息列表，每个文件信息中包含'hash'键
        """
        if not file_info_list:
            logger.warning('没有文件需要计算哈希值')
            return []
            
        logger.info(f'开始并行计算哈希值，共 {len(file_info_list)} 个文件，线程数: {self.max_workers}，算法: {self.hash_algorithm}')
        start_time = time.time()
        
        total_files = len(file_info_list)
        processed_files = 0
        
        # 使用线程池并行计算哈希值
        with ThreadPoolExecutor(max_workers=self.max_workers) as executor:
            # 提交所有哈希计算任务
            future_to_file = {
                executor.submit(self._calculate_file_hash, file_info['path']): file_info
                for file_info in file_info_list
                if 'hash' not in file_info or not file_info['hash']
            }
            
            # 处理完成的任务
            for future in as_completed(future_to_file):
                file_info = future_to_file[future]
                try:
                    # 获取哈希计算结果
                    file_hash = future.result()
                    file_info['hash'] = file_hash
                    
                    if file_hash:
                        logger.debug(f'文件哈希计算完成: {file_info["path"]}, 哈希值: {file_hash}')
                except Exception as e:
                    logger.error(f'计算文件哈希值时出错: {file_info["path"]}, 错误: {str(e)}')
                    file_info['hash'] = ''
                
                # 更新进度
                processed_files += 1
                if progress_callback:
                    progress_callback(processed_files, total_files)
                
                # 检查是否需要停止
                if stop_event and stop_event():
                    logger.info('哈希计算操作已停止')
                    executor.shutdown(wait=False, cancel_futures=True)
                    break
        
        end_time = time.time()
        logger.info(f'哈希计算完成，共处理 {processed_files} 个文件，耗时: {end_time - start_time:.2f} 秒')
        
        return file_info_list
        
    def get_supported_algorithms(self) -> list:
        """
        获取支持的哈希算法列表
        :return: 支持的哈希算法列表
        """
        return list(hashlib.algorithms_available)
        
    def set_hash_algorithm(self, algorithm: str) -> bool:
        """
        设置哈希算法
        :param algorithm: 哈希算法名称
        :return: 是否设置成功
        """
        if algorithm in hashlib.algorithms_available:
            self.hash_algorithm = algorithm
            logger.info(f'哈希算法已设置为: {algorithm}')
            return True
        else:
            logger.error(f'不支持的哈希算法: {algorithm}')
            return False